//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package webhook_serv_gitlab

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"atomgit.com/openlinksaas/api-server/dao/event_dao"
	"atomgit.com/openlinksaas/proto-gen-go.git/events_api"
	gitlab_hook "atomgit.com/openlinksaas/webhook.git/gitlab"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"google.golang.org/protobuf/types/known/anypb"
)

var sourceType zapcore.Field

func init() {
	sourceType = zap.String("sourceType", "gitlab")
}

func Process(projectId, eventSourceId string, header http.Header, payload []byte, secret string, logger *zap.Logger) (*event_dao.EventStageDbItem, *anypb.Any, error) {
	token := header.Get("X-Gitlab-Token")
	if token != secret {
		logger.Error("wrong webhook token", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, fmt.Errorf("wrong event token")
	}
	timeStamp := time.Now().UnixNano() / 1e6
	eventStr := header.Get("X-Gitlab-Event")
	if eventStr == "Push Hook" {
		return processPush(projectId, eventSourceId, payload, timeStamp, logger)
	} else if eventStr == "Tag Push Hook" {
		return processTag(projectId, eventSourceId, payload, timeStamp, logger)
	} else if eventStr == "Issue Hook" || eventStr == "Confidential Issue Hook" {
		return processIssue(projectId, eventSourceId, payload, timeStamp, logger)
	} else if eventStr == "Note Hook" {
		return processComment(projectId, eventSourceId, payload, timeStamp, logger)
	} else if eventStr == "Merge Request Hook" {
		return processMerge(projectId, eventSourceId, payload, timeStamp, logger)
	} else if eventStr == "Wiki Page Hook" {
		return processWiki(projectId, eventSourceId, payload, timeStamp, logger)
	} else if eventStr == "Pipeline Hook" {
		return processPipeline(projectId, eventSourceId, payload, timeStamp, logger)
	} else if eventStr == "Build Hook" {
		return processBuild(projectId, eventSourceId, payload, timeStamp, logger)
	} else if eventStr == "Job Hook" {
		return processJob(projectId, eventSourceId, payload, timeStamp, logger)
	} else {
		logger.Error("wrong webhook event type", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, fmt.Errorf("unkwown event data")
	}
}

func processPush(projectId, eventSourceId string, payload []byte, timeStamp int64, logger *zap.Logger) (*event_dao.EventStageDbItem, *anypb.Any, error) {
	logger.Info("recive push event", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
	pushHook := &gitlab_hook.PushHook{}
	err := json.Unmarshal(payload, pushHook)
	if err != nil {
		logger.Error("unmarshal failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType, zap.String("payload", string(payload)))
		return nil, nil, err
	}
	issueEvent := pushHook.ToProtoBuf()
	eventAny, err := anypb.New(issueEvent)
	if err != nil {
		logger.Error("convert event failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, err
	}
	sourceUserName := pushHook.UserUsername
	sourceDisplayName := pushHook.UserName
	stageItem := &event_dao.EventStageDbItem{
		ProjectId:         projectId,
		EventSourceId:     eventSourceId,
		SourceUserName:    sourceUserName,
		SourceDisplayName: sourceDisplayName,
		EventType:         uint32(events_api.EVENT_TYPE_EVENT_TYPE_GITLAB),
		EventTime:         timeStamp,
	}
	return stageItem, eventAny, nil
}

func processTag(projectId, eventSourceId string, payload []byte, timeStamp int64, logger *zap.Logger) (*event_dao.EventStageDbItem, *anypb.Any, error) {
	logger.Info("recive tag push event", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
	tagHook := &gitlab_hook.TagHook{}
	err := json.Unmarshal(payload, tagHook)
	if err != nil {
		logger.Error("unmarshal failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType, zap.String("payload", string(payload)))
		return nil, nil, err
	}
	issueEvent := tagHook.ToProtoBuf()
	eventAny, err := anypb.New(issueEvent)
	if err != nil {
		logger.Error("convert event failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, err
	}
	sourceUserName := tagHook.UserUsername
	sourceDisplayName := tagHook.UserName
	stageItem := &event_dao.EventStageDbItem{
		ProjectId:         projectId,
		EventSourceId:     eventSourceId,
		SourceUserName:    sourceUserName,
		SourceDisplayName: sourceDisplayName,
		EventType:         uint32(events_api.EVENT_TYPE_EVENT_TYPE_GITLAB),
		EventTime:         timeStamp,
	}
	return stageItem, eventAny, nil
}

func processIssue(projectId, eventSourceId string, payload []byte, timeStamp int64, logger *zap.Logger) (*event_dao.EventStageDbItem, *anypb.Any, error) {
	logger.Info("recive issue event", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
	issueHook := &gitlab_hook.IssueHook{}
	err := json.Unmarshal(payload, issueHook)
	if err != nil {
		logger.Error("unmarshal failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType, zap.String("payload", string(payload)))
		return nil, nil, err
	}
	issueEvent := issueHook.ToProtoBuf()
	eventAny, err := anypb.New(issueEvent)
	if err != nil {
		logger.Error("convert event failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, err
	}
	sourceUserName := issueHook.User.UserName
	sourceDisplayName := issueHook.User.Name
	stageItem := &event_dao.EventStageDbItem{
		ProjectId:         projectId,
		EventSourceId:     eventSourceId,
		SourceUserName:    sourceUserName,
		SourceDisplayName: sourceDisplayName,
		EventType:         uint32(events_api.EVENT_TYPE_EVENT_TYPE_GITLAB),
		EventTime:         timeStamp,
	}
	return stageItem, eventAny, nil
}

func processComment(projectId, eventSourceId string, payload []byte, timeStamp int64, logger *zap.Logger) (*event_dao.EventStageDbItem, *anypb.Any, error) {
	logger.Info("recive note event", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
	commentHook := &gitlab_hook.CommentHook{}
	err := json.Unmarshal(payload, commentHook)
	if err != nil {
		logger.Error("unmarshal failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType, zap.String("payload", string(payload)))
		return nil, nil, err
	}
	issueEvent := commentHook.ToProtoBuf()
	eventAny, err := anypb.New(issueEvent)
	if err != nil {
		logger.Error("convert event failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, err
	}
	sourceUserName := commentHook.User.UserName
	sourceDisplayName := commentHook.User.Name
	stageItem := &event_dao.EventStageDbItem{
		ProjectId:         projectId,
		EventSourceId:     eventSourceId,
		SourceUserName:    sourceUserName,
		SourceDisplayName: sourceDisplayName,
		EventType:         uint32(events_api.EVENT_TYPE_EVENT_TYPE_GITLAB),
		EventTime:         timeStamp,
	}
	return stageItem, eventAny, nil
}

func processMerge(projectId, eventSourceId string, payload []byte, timeStamp int64, logger *zap.Logger) (*event_dao.EventStageDbItem, *anypb.Any, error) {
	logger.Info("recive merge request event", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
	mrHook := &gitlab_hook.MergeRequestHook{}
	err := json.Unmarshal(payload, mrHook)
	if err != nil {
		logger.Error("unmarshal failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType, zap.String("payload", string(payload)))
		return nil, nil, err
	}
	issueEvent := mrHook.ToProtoBuf()
	eventAny, err := anypb.New(issueEvent)
	if err != nil {
		logger.Error("convert event failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, err
	}
	sourceUserName := mrHook.User.UserName
	sourceDisplayName := mrHook.User.Name
	stageItem := &event_dao.EventStageDbItem{
		ProjectId:         projectId,
		EventSourceId:     eventSourceId,
		SourceUserName:    sourceUserName,
		SourceDisplayName: sourceDisplayName,
		EventType:         uint32(events_api.EVENT_TYPE_EVENT_TYPE_GITLAB),
		EventTime:         timeStamp,
	}
	return stageItem, eventAny, nil
}

func processWiki(projectId, eventSourceId string, payload []byte, timeStamp int64, logger *zap.Logger) (*event_dao.EventStageDbItem, *anypb.Any, error) {
	logger.Info("recive wiki event", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
	wikiHook := &gitlab_hook.WikiHook{}
	err := json.Unmarshal(payload, wikiHook)
	if err != nil {
		logger.Error("unmarshal failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType, zap.String("payload", string(payload)))
		return nil, nil, err
	}
	issueEvent := wikiHook.ToProtoBuf()
	eventAny, err := anypb.New(issueEvent)
	if err != nil {
		logger.Error("convert event failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, err
	}
	sourceUserName := wikiHook.User.UserName
	sourceDisplayName := wikiHook.User.Name
	stageItem := &event_dao.EventStageDbItem{
		ProjectId:         projectId,
		EventSourceId:     eventSourceId,
		SourceUserName:    sourceUserName,
		SourceDisplayName: sourceDisplayName,
		EventType:         uint32(events_api.EVENT_TYPE_EVENT_TYPE_GITLAB),
		EventTime:         timeStamp,
	}
	return stageItem, eventAny, nil
}

func processPipeline(projectId, eventSourceId string, payload []byte, timeStamp int64, logger *zap.Logger) (*event_dao.EventStageDbItem, *anypb.Any, error) {
	logger.Info("recive pipe line event", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
	pipeHook := &gitlab_hook.PipelineHook{}
	err := json.Unmarshal(payload, pipeHook)
	if err != nil {
		logger.Error("unmarshal failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType, zap.String("payload", string(payload)))
		return nil, nil, err
	}
	issueEvent := pipeHook.ToProtoBuf()
	eventAny, err := anypb.New(issueEvent)
	if err != nil {
		logger.Error("convert event failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, err
	}
	sourceUserName := pipeHook.User.UserName
	sourceDisplayName := pipeHook.User.Name
	stageItem := &event_dao.EventStageDbItem{
		ProjectId:         projectId,
		EventSourceId:     eventSourceId,
		SourceUserName:    sourceUserName,
		SourceDisplayName: sourceDisplayName,
		EventType:         uint32(events_api.EVENT_TYPE_EVENT_TYPE_GITLAB),
		EventTime:         timeStamp,
	}
	return stageItem, eventAny, nil
}

func processBuild(projectId, eventSourceId string, payload []byte, timeStamp int64, logger *zap.Logger) (*event_dao.EventStageDbItem, *anypb.Any, error) {
	logger.Info("recive build event", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
	buildHook := &gitlab_hook.BuildHook{}
	err := json.Unmarshal(payload, buildHook)
	if err != nil {
		logger.Error("unmarshal failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType, zap.String("payload", string(payload)))
		return nil, nil, err
	}
	issueEvent := buildHook.ToProtoBuf()
	eventAny, err := anypb.New(issueEvent)
	if err != nil {
		logger.Error("convert event failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, err
	}
	sourceUserName := buildHook.User.UserName
	sourceDisplayName := buildHook.User.Name
	stageItem := &event_dao.EventStageDbItem{
		ProjectId:         projectId,
		EventSourceId:     eventSourceId,
		SourceUserName:    sourceUserName,
		SourceDisplayName: sourceDisplayName,
		EventType:         uint32(events_api.EVENT_TYPE_EVENT_TYPE_GITLAB),
		EventTime:         timeStamp,
	}
	return stageItem, eventAny, nil
}

func processJob(projectId, eventSourceId string, payload []byte, timeStamp int64, logger *zap.Logger) (*event_dao.EventStageDbItem, *anypb.Any, error) {
	logger.Info("recive job event", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
	jobHook := &gitlab_hook.JobHook{}
	err := json.Unmarshal(payload, jobHook)
	if err != nil {
		logger.Error("unmarshal failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType, zap.String("payload", string(payload)))
		return nil, nil, err
	}
	issueEvent := jobHook.ToProtoBuf()
	eventAny, err := anypb.New(issueEvent)
	if err != nil {
		logger.Error("convert event failed", zap.String("projectId", projectId), zap.String("eventSourceId", eventSourceId), sourceType)
		return nil, nil, err
	}
	sourceUserName := jobHook.User.UserName
	sourceDisplayName := jobHook.User.Name
	stageItem := &event_dao.EventStageDbItem{
		ProjectId:         projectId,
		EventSourceId:     eventSourceId,
		SourceUserName:    sourceUserName,
		SourceDisplayName: sourceDisplayName,
		EventType:         uint32(events_api.EVENT_TYPE_EVENT_TYPE_GITLAB),
		EventTime:         timeStamp,
	}
	return stageItem, eventAny, nil
}
